ao
でのトークンの構築
トークンを作成する際には、ao
内でLua言語を引き続き使用してトークンをミントします。これは、トークンスペシフィケーションで概説された原則に従います。
トークンを作成する2つの方法:
1 - トークンブループリントを使用する:
.load-blueprint token
トークンブループリントを使用すると、すでにすべてのハンドラと状態が定義されたトークンが作成されます。これはトークンを作成する最も簡単な方法です。ブループリントを読み込んだ後、これらのハンドラと状態をカスタマイズすることができます。
利用可能なブループリントについてさらに学ぶには、こちらを参照してください: ブループリント
INFO
トークンブループリントを使用すると迅速にトークンが作成できますが、ニーズに合わせてカスタマイズできるように、トークンの読み込みとテスト方法も理解しておく必要があります。
2 - ゼロから構築する:
以下のガイドでは、ゼロからトークンを作成するプロセスを案内します。これはトークンを作成するより高度な方法ですが、トークンの仕組みをより深く理解するのに役立ちます。
準備
ステップ1: トークンの初期化
- 好きなテキストエディタを開きます。できれば、前回のチュートリアルで使用したフォルダー内から開いてください。
token.lua
という新しいファイルを作成します。token.lua
内で、トークンの状態を初期化し、残高、名前、ティッカーなどを定義します。
local json = require('json')
if not Balances then Balances = { [ao.id] = 100000000000000 } end
if Name ~= 'My Coin' then Name = 'My Coin' end
if Ticker ~= 'COIN' then Ticker = 'COIN' end
if Denomination ~= 10 then Denomination = 10 end
if not Logo then Logo = 'optional arweave TXID of logo image' end
ここで行ったことを分解してみましょう:
local json = require('json')
: このコードの最初の行は、後で使用するためのモジュールをインポートします。if not Balances then Balances = { [ao.id] = 100000000000000 } end
: この2行目は、トークンを所有する人を追跡するためのBalancesテーブルを初期化しています。トークンプロセスao.id
を初期状態で全残高を持つように初期化します。次の4行、
if Name
、if Ticker
、if Denomination
、if not Logo
はすべてオプションですが、if Denomination
を除いて、トークンの名前、ティッカー、単位、ロゴをそれぞれ定義するために使用されます。
INFO
コードif Denomination ~= 10 then Denomination = 10 end
は、単位として扱うべきトークンの数を示しています。
ステップ2: InfoおよびBalancesハンドラ
受信メッセージハンドラ
次に、受信メッセージを処理するための最初のハンドラを追加しましょう。
Handlers.add('info', Handlers.utils.hasMatchingTag('Action', 'Info'), function(msg)
ao.send(
{ Target = msg.From, Tags = { Name = Name, Ticker = Ticker, Logo = Logo, Denomination = tostring(Denomination) } })
end)
INFO
この時点で、私たちがすべてのハンドラをtoken.lua
ファイル内に構築していることに気付いたかもしれません。.editor
を使用していない理由です。
多くのハンドラやプロセスがある場合、.editor
を使用してハンドラを作成することは完全に問題ありませんが、トークンの初期化、情報および残高ハンドラ、転送ハンドラ、ミントハンドラを設定する完全なプロセスを作成しているため、すべてを1つのファイルにまとめるのが最善です。
これにより、一貫性を保つこともできます。なぜなら、token.lua
ファイルをaos
に再読み込みするたびに、各ハンドラが更新されるからです。
このコードは、誰かがタグAction = "info"
のメッセージを送信した場合、私たちのトークンが上記で定義されたすべての情報を含むメッセージを返すことを意味します。Target = msg.From
に注意してください。これは、aoがこのメッセージを送信したプロセスに返信していることを示します。
情報およびトークン残高ハンドラ
次に、トークンの残高に関する情報を提供する2つのハンドラを追加できます。
Handlers.add('balance', Handlers.utils.hasMatchingTag('Action', 'Balance'), function(msg)
local bal = '0'
-- If not Target is provided, then return the Senders balance
if (msg.Tags.Target and Balances[msg.Tags.Target]) then
bal = tostring(Balances[msg.Tags.Target])
elseif Balances[msg.From] then
bal = tostring(Balances[msg.From])
end
ao.send({
Target = msg.From,
Tags = { Target = msg.From, Balance = bal, Ticker = Ticker, Data = json.encode(tonumber(bal)) }
})
end)
Handlers.add('balances', Handlers.utils.hasMatchingTag('Action', 'Balances'),
function(msg) ao.send({ Target = msg.From, Data = json.encode(Balances) }) end)
最初のハンドラHandlers.add('balance'
は、自分の残高またはターゲットの残高を要求しているプロセスや人に対応します。その後、情報を含むメッセージで返信します。2つ目のハンドラHandlers.add('balances'
は、残高テーブル全体で返信します。
ステップ3: 転送ハンドラ
テストを開始する前に、プロセスやユーザー間でトークンを転送するための2つのハンドラを追加します。
Handlers.add('transfer', Handlers.utils.hasMatchingTag('Action', 'Transfer'), function(msg)
assert(type(msg.Tags.Recipient) == 'string', 'Recipient is required!')
assert(type(msg.Tags.Quantity) == 'string', 'Quantity is required!')
if not Balances[msg.From] then Balances[msg.From] = 0 end
if not Balances[msg.Tags.Recipient] then Balances[msg.Tags.Recipient] = 0 end
local qty = tonumber(msg.Tags.Quantity)
assert(type(qty) == 'number', 'qty must be number')
if Balances[msg.From] >= qty then
Balances[msg.From] = Balances[msg.From] - qty
Balances[msg.Tags.Recipient] = Balances[msg.Tags.Recipient] + qty
--[[
Only Send the notifications to the Sender and Recipient
if the Cast tag is not set on the Transfer message
]] --
if not msg.Tags.Cast then
-- Debit-Notice message template, that is sent to the Sender of the transfer
local debitNotice = {
Target = msg.From,
Action = 'Debit-Notice',
Recipient = msg.Recipient,
Quantity = tostring(qty),
Data = Colors.gray ..
"You transferred " ..
Colors.blue .. msg.Quantity .. Colors.gray .. " to " .. Colors.green .. msg.Recipient .. Colors.reset
}
-- Credit-Notice message template, that is sent to the Recipient of the transfer
local creditNotice = {
Target = msg.Recipient,
Action = 'Credit-Notice',
Sender = msg.From,
Quantity = tostring(qty),
Data = Colors.gray ..
"You received " ..
Colors.blue .. msg.Quantity .. Colors.gray .. " from " .. Colors.green .. msg.From .. Colors.reset
}
-- Add forwarded tags to the credit and debit notice messages
for tagName, tagValue in pairs(msg) do
-- Tags beginning with "X-" are forwarded
if string.sub(tagName, 1, 2) == "X-" then
debitNotice[tagName] = tagValue
creditNotice[tagName] = tagValue
end
end
-- Send Debit-Notice and Credit-Notice
ao.send(debitNotice)
ao.send(creditNotice)
end
else
ao.send({
Target = msg.Tags.From,
Tags = { Action = 'Transfer-Error', ['Message-Id'] = msg.Id, Error = 'Insufficient Balance!' }
})
end
end)
このコードは、受取人と数量のタグが提供されていることを確認し、メッセージを送信している人と受取人の残高を初期化します(存在しない場合)そして、指定された数量を残高テーブル内の受取人に転送しようとします。
Balances[msg.From] = Balances[msg.From] - qty
Balances[msg.Tags.Recipient] = Balances[msg.Tags.Recipient] + qty
転送が成功した場合、元のメッセージの送信者にデビット通知が送信され、受取人にはクレジット通知が送信されます。
-- Send Debit-Notice to the Sender
ao.send({
Target = msg.From,
Tags = { Action = 'Debit-Notice', Recipient = msg.Tags.Recipient, Quantity = tostring(qty) }
})
-- Send Credit-Notice to the Recipient
ao.send({
Target = msg.Tags.Recipient,
Tags = { Action = 'Credit-Notice', Sender = msg.From, Quantity = tostring(qty) }
})
転送のための残高が不足している場合、失敗メッセージを返します。
ao.send({
Target = msg.Tags.From,
Tags = { Action = 'Transfer-Error', ['Message-Id'] = msg.Id, Error = 'Insufficient Balance!' }
})
if not msg.Tags.Cast then
という行は、キャストタグが設定されている場合、メッセージをプッシュしないことを意味します。これはaoプロトコルの一部です。
ステップ4: ミントハンドラ
最後に、新しいトークンをミントするためのハンドラを追加します。
Handlers.add('mint', Handlers.utils.hasMatchingTag('Action', 'Mint'), function(msg, env)
assert(type(msg.Tags.Quantity) == 'string', 'Quantity is required!')
if msg.From == env.Process.Id then
-- Add tokens to the token pool, according to Quantity
local qty = tonumber(msg.Tags.Quantity)
Balances[env.Process.Id] = Balances[env.Process.Id] + qty
else
ao.send({
Target = msg.Tags.From,
Tags = {
Action = 'Mint-Error',
['Message-Id'] = msg.Id,
Error = 'Only the Process Owner can mint new ' .. Ticker .. ' tokens!'
}
})
end
end)
このコードは、数量タグが提供されていることを確認し、指定された数量を残高テーブルに追加します。
ロードとテスト
token.lua
ファイルを作成したら、または.load-blueprint token
を使用したら、テストを開始する準備が整いました。
1 - aosプロセスを開始
ターミナルでaos
を実行して、aosプロセスを開始したことを確認してください。
2 - token.luaファイルをロード
ガイドに従った場合、aosプロセスと同じディレクトリにtoken.lua
ファイルがあるはずです。aosプロンプトからファイルをロードします。
.load token.lua
3 - トークンのテスト
今、aosプロセスIDにメッセージを送信して動作しているかどうか確認できます。ターゲットにao.id
を使用すると、自分自身にメッセージを送信していることになります。
Send({ Target = ao.id, Action = "Info" })
これにより、契約で定義された情報が表示されるはずです。最新の受信ボックスメッセージで応答を確認してください。
Inbox[#Inbox].Tags
契約で定義された情報が表示されるはずです。
INFO
最後のメッセージを数値で確認することを忘れないでください。そのために、まず#Inbox
を実行して受信ボックス内のメッセージの合計数を確認します。その後、最後のメッセージ番号を実行してデータを確認します。
Example:
If #Inbox
returns 5
, then run Inbox[5].Data
to see the data. :::
4 - 転送
次に、他のウォレットまたはプロセスIDにトークンの残高を転送してみてください。
INFO
別のプロセスIDが必要な場合は、別のターミナルウィンドウでaos [name]
を実行して新しいプロセスIDを取得できます。同じaos [name]
ではないことを確認してください。
Example:
If you're using aos
in one terminal window, you can run aos test
in another terminal window to get a new process id. :::
Send({ Target = ao.id, Tags = { Action = "Transfer", Recipient = 'another wallet or processid', Quantity = '10000' }})
送信後、ターミナルにDebit-Notice
が送信者側に、Credit-Notice
が受取人側に表示されるメッセージが印刷されます。
5 - 残高を確認
トークンを転送したので、残高を確認してみましょう。
Send({ Target = ao.id, Tags = { Action = "Balances" }})
Inbox[#Inbox].Data
2つのプロセスIDまたはウォレットアドレスが表示され、それぞれが残高を表示します。最初は送信プロセスID、2番目は受取人のプロセスIDである必要があります。
6 - トークンのミント
最後に、いくつかのトークンをミントしようとしてください。
Send({ Target = ao.id, Tags = { Action = "Mint", Quantity = '1000' }})
And check the balances again.
Send({ Target = ao.id, Tags = { Action = "Balances" }})
Inbox[#Inbox].Data
その後、トークンをミントしたプロセスIDの残高が増加したことが確認できます。
結論
これで「トークンを作成する」ガイドは終了です。カスタムトークンを作成する方法を学ぶことで、プロジェクトに大きな可能性が開かれます。それは新しい通貨を作成すること、ゲーム用のトークンを作成すること、ガバナンストークンを作成すること、または想像できるすべてのことです。